package mazeAssignment;

public class CompactArrayMaze implements Maze {
	private static final int NORTH_WALL = 0x0100;
	private static final int EAST_WALL = 0x0200;
	private static final int SOUTH_WALL = 0x0400;
	private static final int WEST_WALL = 0x0800;

	private static final int BUILD_MASK = 0x000F;
	private static final int SOLVE_MASK = 0x00F0;
	
	//Build state (int range 0 - 15)
	//0x0001
	//0x0002
	//0x0004
	//0x0008
	
	//Solve state (int range 0 - 15)
	//0x0010
	//0x0020
	//0x0040
	//0x0080
	
	//Walls
	//0x0100
	//0x0200
	//0x0400
	//0x0800
	
	//Unused
	//0x1000
	//0x2000
	//0x4000
	//0x8000
	
    private int xSize, ySize;
    private int startX, startY;
    private int endX, endY;
    private int[] mazeGrid;
    
    public CompactArrayMaze(int x, int y) {
	    mazeGrid = new int[x*y];
	    xSize= x;
	    ySize= y;
	    clear();
    }
    
    private int index(int x, int y) {
    	return x*ySize + y;
    }
    
	@Override
	public void clear() {
		int empty_maze_element = NORTH_WALL | EAST_WALL | SOUTH_WALL | WEST_WALL;
		for (int i = 0; i < mazeGrid.length; i++) {
			mazeGrid[i] = empty_maze_element;
		}
	    startX = -1;
	    startY = -1;
	    endX = -1;
	    endY = -1;
	}
	@Override
	public void clearSolveState() {
		for (int i = 0; i < mazeGrid.length; i++) {
			mazeGrid[i] = mazeGrid[i] & (~SOLVE_MASK);
		}
	}
	@Override
	public int getSizeX() {
		return xSize;
	}
	@Override
	public int getSizeY() {
		return ySize;
	}
	@Override
	public void visit(int x, int y) {
		setSolveState(x, y, Maze.SOLVE_VISITED);
	}
	@Override
	public void markAsInvalid(int x, int y) {
		setSolveState(x, y, Maze.SOLVE_INVALID);
	}
	@Override
	public boolean isVisited(int x, int y) {
		int solveState = getSolveState(x, y);
		return (solveState != Maze.SOLVE_UNVISITED && solveState != Maze.SOLVE_END);
	}
	
	private boolean getWall(int x, int y, int wall) {
		return (mazeGrid[index(x, y)] & wall) != 0;
	}
	
	@Override
	public boolean getWest(int x, int y) {
		return getWall(x, y, WEST_WALL);
	}
	@Override
	public boolean getEast(int x, int y) {
		return getWall(x, y, EAST_WALL);
	}
	@Override
	public boolean getNorth(int x, int y) {
		return getWall(x, y, NORTH_WALL);
	}
	@Override
	public boolean getSouth(int x, int y) {
		return getWall(x, y, SOUTH_WALL);
	}
	
	private void setWall(int x, int y, boolean state, int wall) {
		incrementBuildState(x, y);
		if(state) {
			mazeGrid[index(x, y)] |= wall;
		}
		else {
			mazeGrid[index(x, y)] &= (~wall);
		}
	}
	
	@Override
	public void setNorth(int x, int y, boolean state) {
		setWall(x, y, state, NORTH_WALL);
	}
	@Override
	public void setSouth(int x, int y, boolean state) {
		setWall(x, y, state, SOUTH_WALL);
	}
	@Override
	public void setEast(int x, int y, boolean state) {
		setWall(x, y, state, EAST_WALL);
	}
	@Override
	public void setWest(int x, int y, boolean state) {
		setWall(x, y, state, WEST_WALL);
	}
	@Override
	public void setSolveState(int x, int y, int sState) {
		if (sState == Maze.SOLVE_START) {
			startX = x;
			startY = y;
		}
		else if(sState == Maze.SOLVE_END) {
			endX = x;
			endY = y;
		}
		else {
			int index = index(x, y);
			mazeGrid[index] = (mazeGrid[index] & (~SOLVE_MASK)) + sState;
		}
	}
	
	@Override
	public int getSolveState(int x, int y) {
		if (x == startX && y == startY) {
			return Maze.SOLVE_START;
		}
		else if(x == endX && y == endY) {
			return Maze.SOLVE_END;
		}
		return mazeGrid[index(x, y)] & SOLVE_MASK;
	}
	
	private void incrementBuildState(int x, int y) {
		int buildState = getBuildState(x, y);
		buildState++;
		if(buildState < 0 || buildState > 15) {
			throw new RuntimeException("Encountered invalid buildstate '"+ buildState +".");
		}
		mazeGrid[index(x, y)] = (mazeGrid[index(x, y)] & (~BUILD_MASK)) + buildState; 
	}
	
	@Override
	public int getBuildState(int x, int y) {
		return mazeGrid[index(x, y)] & BUILD_MASK;
	}
}
